<?php
/**
 * 悠络客摄像头
 */

namespace App\Kuafu\Shop\Service\CameraLibrary;

use App\Kuafu\Shop\Enum\CodeEnum;
use App\Library\FeiShu\SendFs;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use KuafuComposer\ApiTime\Service\ApiTimeService;
use Symfony\Component\HttpFoundation\Request as RequestAlias;

class YlkCamera extends CameraBase implements CameraInterface
{
    private string $host = 'https://open.umtrix.com';

    private string $appid;

    private string $secret;

    private string $authorization;

    private string $di; // device_id

    private string $sn; // serial_no

    private string $cn; // channel_id

    private string $storeId; // channel_id

    private string $cacheKey = 'cache:camera:ylk'; // 缓存 key

    public function init(): void
    {
        $this->di      = $this->cameraModel->deviceName ?? '';
        $this->sn      = $this->cameraModel->serialNo ?? '';
        $this->cn      = $this->cameraModel->productKey ?? '';
        $this->appid   = config('camera.ylk.appid');
        $this->secret  = config('camera.ylk.secret');
        $this->storeId = $this->shopInfo->cameraSupplierId ?? '';
    }

    /**
     * 直播流
     *
     * @param string $scheme
     * @param int $rate
     * @return array|string[]
     */
    public function getLiveStreaming(
        string $scheme,
        int $clarity = CodeEnum::CLARITY_TYPE_FLUENCY
    ): array {
        $uri    = '/h/KeNcI/auth/weburl';
        $rate   = self::switchClarity($clarity);
        $scheme = self::switchScheme($scheme);

        $query = [
            'device_id'    => $this->sn,
            'channel_idx'  => $this->cn,
            'rate'         => $rate,
            'play_type'    => $scheme,
            'enable_audio' => 0 // 是否支持声音。1是 ，0否，不传默认值 1
        ];

        $data = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
        return [
            'url' => $data['play_url'] ?? ''
        ];
    }

    /**
     * 心跳接口
     *
     * @param string $type
     * @param string $playType
     * @param int $rate
     * @return array
     */
    public function heartBeat(string $type = '1', string $playType = 'hls', int $rate = 1000): array
    {
        $uri      = '/h/KeNfI/auth/media_heartbeat';
        $playType = self::switchScheme($playType);

        $query = [
            'type'      => $type,
            // 1-实时，2-回放
            'play_type' => $playType,
            // 播放类型。->> 可选项 [hflv，hls，rtmp, s_hflv, s_hls]
            'source'    => $this->sn . '-' . $this->cn . '-' . $rate
            // *播放请求时会返回source，取返回source的值；没有返回source字段，就用播放请求的入参字段拼接下 device_id-channel_idx-rate
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }

    /**
     * 设备状态
     *
     * @return int
     */
    public function getDeviceStatus(): int
    {
        try {
            $result = $this->storeDeviceListWithStatus();
            // 获取渠道号
            $first = reset($result);
            if (!empty($first['channel'])) {
                $channelList = $first['channel'];
                $channelList = array_column($channelList, null, 'channel_id');
                $status      = $channelList[$this->cn]['status'] ?? 0;
            } else {
                $status = 0;
            }
            return (int)$status;
        } catch (\Exception $exception) {
            $this->logErr(__FUNCTION__, $result ?? [], $exception);
            return 0;
        }
    }

    /**
     * 获取设备状态
     *
     * @param array $cameraList
     * @return array []
     */
    public function getDeviceStatusBatch(array $cameraList): array
    {
        try {
            $result = $this->storeDeviceListWithStatus();
            // 判断是外接设备还是直连设备
            if (count($result) == 1) {
                $first       = reset($result);
                $channelList = $first['channel'];
                $channelList = array_column($channelList, null, 'channel_id');
                foreach ($cameraList as &$value) {
                    $value['status'] = $channelList[$value['productKey']]['status'] ?? 0;
                }
            } else {
                $result = array_column($result, null, 'sn');
                foreach ($cameraList as &$value) {
                    if (!empty($result[$value['serialNo']])) {
                        if (count($result[$value['serialNo']]) > 0) {
                            $channelIdList   = collect($result[$value['serialNo']]['channel'])->keyBy('channel_id');
                            $value['status'] = $channelIdList[$value['productKey']]['status'] ?? 0;
                        } else {
                            $value['status'] = $result[$value['serialNo']]['channel'][0]['status'] ?? 0;
                        }
                    }
                }
            }
            return $cameraList;
        } catch (\Exception $exception) {
            $this->logErr(__FUNCTION__, $result, $exception);
            return [];
        }
    }

    /**
     * 获取视频回放url
     */
    public function backUrl(int $startAt, int $endAt, int $seekTime, string $scheme = 'flv'): array
    {
        $uri    = '/h/KeOcw/playweb/back_url';
        $scheme = self::switchScheme($scheme);

        $query = [
            'sn'         => $this->sn, // 设备SN(必须是nvr或是带TF(SD)卡的设备)。
            'channel_id' => $this->cn, // 通道号，起始通道号从1开始
            'start_time' => $startAt + $seekTime, // 开始时间。Unix时间戳，到秒。
            'play_type'  => $scheme, // 播放类型。->> 可选项 [hflv，hls，rtmp, s_hflv, s_hls]
            'rate'       => 1000, // 通道号。默认为 1
        ];

        $data = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_POST, $query));

        return [
            'url'         => $data['play_url'] ?? '',
            'domain_list' => $data['domain_list'] ?? ''
        ];
    }



    /**
     * 获取用户权限内的所有门店列表和详情
     *
     * @param string $groupId
     * @return array
     */
    public function visibleStores(string $groupId): array
    {
        $uri = '/h/LpGAe/auth/visible_stores';  //返回企业的所有门店组节点的id和名称，不含门店节点．

        $query = [
            'group_id' => $groupId,
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }

    /**
     * 根据门店id、小时等条件查询门店客流统计数据。
     *
     * @param string $date
     * @return array
     */
    public function getStatisticHourlySummary(string $date = '')
    {
        $uri = '/h/D2dmf/keliu/statistic_hourly_summary';  //返回企业的所有门店组节点的id和名称，不含门店节点．

        $date = $date ?: date('Y-m-d');

        $query = [
            'av'       => '1',
            'store_id' => $this->storeId,
            'date'     => $date,
        ];
        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }


    /**
     * 门店视频列表（含NVR/IPC）
     *
     * @param string $storeId
     * @return array
     */
    public function storeDeviceChannelList(string $storeId): array
    {
        $uri = '/h/LpGAh/auth/store_device_channel_list';

        $query = [
            'av'       => '1.1',
            'store_id' => $storeId,
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }


    /**
     * 返回数据
     *
     * @param string $uri
     * @param string $method
     * @param array $param
     * @param int $cacheTime
     * @return array|mixed
     */
    public function curl(string $uri = '', string $method = '', array $param = [], int $cacheTime = 0): array
    {
        try {
            return Cache::remember($this->genCacheKey($uri, $param), getSwitchCacheTime($cacheTime),
                function () use ($uri, $method, $param, $cacheTime) {
                    if (empty($param['av'])) {
                        $param['av'] = '1'; // 固定写死 1
                    }

                    $this->genSign($uri, $param);

                    $client = new Client(['http_errors' => false]);
                    $logUri = $requestUri = $this->host . $uri;

                    $header = [
                        'Authorization' => 'Basic ' . $this->authorization,
                        'Content-Type'  => 'application/x-www-form-urlencoded'
                    ];

                    $startMicroTime = microtime(true);

                    $requestUri = strtoupper($method) == RequestAlias::METHOD_GET ? $requestUri . "?" . http_build_query($param) : $requestUri;

                    $request = new Request(
                        $method,
                        $requestUri,
                        $header,
                        null
                    );

                    Log::info('camera-ulk-request:', [
                        'url'       => $requestUri,
                        'header'    => $header,
                        'cacheTime' => $cacheTime,
                    ]);

                    $option = [];
                    if ($method == RequestAlias::METHOD_POST) {
                        $option = [
                            'form_params' => $param
                        ];
                    }

                    $response = $client->send($request, $option);

                    try {
                        (new ApiTimeService('ylkCamera'))->add($logUri, $startMicroTime);
                    } catch (\Exception $e) {
                        Log::warning($e);
                    }

                    $returnStr = $response->getBody()->getContents();

                    Log::info('camera-ulk-request-res:', [
                        'url'       => $requestUri,
                        'header'    => $header,
                        'cacheTime' => $cacheTime,
                        'returnStr' => $returnStr,
                    ]);
                    if (isJsonStr($returnStr)) {
                        return json_decode($returnStr, true);
                    } else {
                        return [];
                    }
                });
        } catch (RequestException $exception) {

            $handelContext = $exception->getHandlerContext();

            if (($handelContext['http_code'] ?? '') == '601') {
                $this->logErr(__FUNCTION__ . ':601 基类请求报错, 接口请求超量了联系 @胡迪', [
                    'requestUri' => $this->host . $uri,
                    'errMessage' => 'over max day request'
                ], $exception);
            } else {
                $this->logErr(__FUNCTION__ . '基类请求报错', [
                    'requestUri' => $this->host . $uri,
                    'errMessage' => $exception->getMessage()
                ], $exception);
            }
            return [];
        } catch (\Exception $exception) {
            $this->logErr(__FUNCTION__ . '基类请求报错', [
                'requestUri' => $this->host . $uri,
                'errMessage' => $exception->getMessage()
            ], $exception);
            return [];
        }
    }

    /**
     * 格式化输出
     *
     * @param array $data
     * @return array
     */
    private function formatReturn(?array $data): array
    {
        if (empty($data)) {
            return [];
        }
        if ($data['code'] === 0) {
            return $data['data'] ?? $data;
        } else {
            return $data;
        }
    }

    /**
     * 缓存 Key
     *
     * @param string $uri
     * @param array $params
     * @return string
     */
    private function genCacheKey(string $uri, array $params = []): string
    {
        return $this->cacheKey . ':' . $uri . ':' . md5($uri . implode(',', $params));
    }

    /**
     * 生成 Authorization
     *
     * @param string $uri
     * @param array $params
     * @return void
     */
    private function genSign(string $uri, array $params = [])
    {
        $queryStr            = http_build_query($params);
        $md5_rand            = $this->secret . $this->appid . $uri . $queryStr;//组成校验因子
        $md5                 = md5($md5_rand);
        $authorization       = base64_encode($this->appid . ':' . $md5);
        $this->authorization = $authorization;
    }

    /**
     * 获取用户权限内的所有门店列表和详情
     *
     * @return array
     */
    public function storeDeviceListWithStatus(): array
    {
        $uri = '/h/LpGAg/auth/store_device_list_with_status';  //返回企业的所有门店组节点的id和名称，不含门店节点．

        $query = [
            'av'       => '1.1',
            'store_id' => $this->storeId,
        ];

        return $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query));
    }

    /**
     * 日志请求
     *
     * @param $title
     * @param $result
     * @param $exception
     * @param array $requestParam
     */
    public function logErr(string $title = '', $result = [], $exception = [], $requestParam = []): void
    {
        SendFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)
              ->setTitleContent('悠络客视频SDK请求报错:' . $title, json_encode([
                  'requestParam' => [
                          'appid'         => $this->appid,
                          'authorization' => $this->authorization,
                          'di'            => $this->di,
                          'sn'            => $this->sn,
                          'cn'            => $this->cn,
                          'storeId'       => $this->storeId,
                      ] + $requestParam,
                  'returnData'   => $result ?? [],
                  'exception'    => $exception
              ]))->send();
    }

    /**
     * 本地录像视频
     *
     * @param int $startAt
     * @param int $endAt
     * @return array
     */
    public function localVideoList(int $startAt, int $endAt): array
    {
        $uri = '/h/Keofj/playweb/local_video_list';

        $query = [
            'device_id'     => $this->sn,
            'channel_index' => $this->cn,
            'start_time'    => $startAt,
            'end_time'      => $endAt,
            'rate'          => '1000',
        ];

        $data = $this->formatReturn($this->curl($uri, RequestAlias::METHOD_GET, $query, 60));

        $return = [];
        if (!empty($data['block_list'])) {
            foreach ($data['block_list'] as $datum) {
                $return [] = [
                    'beginTime' => $datum['start_time'] ?? 0,
                    'endTime'   => $datum['end_time'] ?? 0
                ];
            }
        }

        foreach ($return as &$r) {
            if ($r['beginTime'] < $startAt) {
                $r['beginTime'] = $startAt;
            }

            if ($r['endTime'] > $endAt) {
                $r['endTime'] = $endAt;
            }
        }
        return $return;
    }


    /**
     * 码流切换
     *
     * @param int $clarity
     * @return int
     */
    public function switchClarity(int $clarity = CodeEnum::CLARITY_TYPE_FLUENCY): int
    {
        switch ($clarity) {
            case CodeEnum::CLARITY_TYPE_FLUENCY:
                return 700; // 辅码流
            default:
                return 1000; // 主码流
        }
    }

    /**
     * 悠络客-协议转换
     * @param string $scheme
     * @return string
     */
    public function switchScheme(string $scheme): string
    {
        switch ($scheme) {
            case 'hls':
                $scheme = 's_hls';
                break;
            case 'flv':
                $scheme = 's_hflv';
                break;
        }
        return $scheme;
    }

}
